package com.mt.api.modules.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.mt.common.response.WebPageResponse;
import com.mt.common.response.WebResponse;
import com.mt.common.response.WebResponseUtils;
import com.mt.common.utils.PloverDateUtil;
import com.mt.api.constants.constant.Constants;
import com.mt.api.constants.enums.InstoreStatusEnum;
import com.mt.api.constants.enums.InstoreTypeEnum;
import com.mt.api.modules.convert.AppInstoreConvert;
import com.mt.api.modules.domain.*;
import com.mt.api.modules.domain.bo.AppInstoreAddBo;
import com.mt.api.modules.domain.bo.AppInstoreStatBo;
import com.mt.api.modules.domain.bo.InstoreCancelBo;
import com.mt.api.modules.domain.bo.InstoreStatPageBo;
import com.mt.api.modules.domain.dto.*;
import com.mt.api.modules.domain.vo.*;
import com.mt.api.modules.manager.*;
import com.mt.api.modules.mapper.InstoreMapper;
import com.mt.api.modules.mapstruct.InstoreMapStruct;
import com.mt.api.modules.service.AppInstoreService;
import com.mt.api.util.AmountUtil;
import com.mt.api.util.JwtUserUtils;
import com.mt.api.util.SequenceBuilder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author plover
 * @description 服务实现
 * @date 2020-07-06
 **/
@Service
@RequiredArgsConstructor
@Slf4j
public class AppInstoreServiceImpl extends ServiceImpl<InstoreMapper, Instore> implements AppInstoreService {

    private final AppNewsServiceImpl appNewsService;
    private final ICargoManager cargoManager;
    private final IInstoreManager instoreManager;
    private final IInstoreDetailManager instoreDetailManager;
    private final IStockManager stockManager;
    private final ISkuManager skuManager;
    private final IProductManager productManager;

    private final InstoreMapStruct instoreMapStruct;


    /**
     * @param resources /
     * @return {"cargoNo":"805","colorSizeData":[{"qty":22,"key":"蓝色-L","desc":"蓝色-L"},{"qty":12,"key":"紫色-L","desc":"紫色-L"},{"qty":34,"key":"黑色-L","desc":"黑色-L"},{"qty":35,"key":"黄色-L","desc":"黄色-L"}]}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public WebResponse addInstore(AppInstoreAddBo resources) {
        SequenceBuilder builder = new SequenceBuilder();
        String instoreNo = builder.builderInstoreNo();
        try {
            JwtUserDto jwtUser = JwtUserUtils.getJwtUser();
            List<AppInstoreAddBo.InstoreDetail> instoreDetailList = resources.getInstoreDetails();

            int totalQty = instoreDetailList.stream().flatMap(instoreDetail -> instoreDetail.getSkuInfoList().stream())
                    .filter(simpleSkuInfo -> Objects.nonNull(simpleSkuInfo.getQty()))
                    .mapToInt(AppInstoreAddBo.SimpleSkuInfo::getQty).sum();
            if (totalQty == 0) {
                return WebResponseUtils.error("入库单数量不能为空");
            }

            StringBuilder things = new StringBuilder();
            Date now = new Date();

            //入库保存
            Instore instore = new Instore();
            instore.setInstoreNo(instoreNo);
            instore.setInstoreTime(now);
            instore.setType(InstoreTypeEnum.PRODUCT.getCode());
            instore.setRemark(resources.getRemark());
            instore.setQty(totalQty);
            instoreManager.save(instore);

            for (AppInstoreAddBo.InstoreDetail instoreDetail : instoreDetailList) {
                if (Objects.isNull(instoreDetail.getCargoId())) {
                    continue;
                }
                List<AppInstoreAddBo.SimpleSkuInfo> skuInfoList = instoreDetail.getSkuInfoList();
                for (AppInstoreAddBo.SimpleSkuInfo simpleSkuInfo : skuInfoList) {
                    Integer qty = simpleSkuInfo.getQty();
                    Integer skuId = simpleSkuInfo.getSkuId();
                    if (Objects.isNull(qty) || qty == 0) {
                        continue;
                    }
                    AppSku sku = skuManager.getSkuById(skuId);
                    if (Objects.isNull(sku)) {
                        continue;
                    }

                    things.append(instoreDetail.getCargoNo()).append(",");
                    InstoreDetail svInstoreDetail = new InstoreDetail();
                    svInstoreDetail.setInstoreId(instore.getId());
                    svInstoreDetail.setCargoNo(sku.getCargoNo());
                    svInstoreDetail.setCargoId(sku.getCargoId());
                    svInstoreDetail.setSkuId(skuId);
                    svInstoreDetail.setSkuCode(sku.getSkuCode());
                    svInstoreDetail.setColor(sku.getColor());
                    svInstoreDetail.setSize(sku.getSize());
                    svInstoreDetail.setQty(qty);
                    instoreDetailManager.save(svInstoreDetail);

                    //更新库存
                    AppStock appStock = AppStock.builder().skuCode(sku.getSkuCode()).skuId(skuId)
                            .cargoNo(sku.getCargoNo()).cargoId(sku.getCargoId()).color(sku.getColor()).size(sku.getSize()).qty(qty).totalQty(qty)
                            .build();
                    stockManager.saveOrUpdateStock(appStock);
                }
            }
            handlerPostInstore(instore, totalQty, resources, jwtUser);
        } catch (Exception e) {
            log.error("instore add error，resources:{}", JSON.toJSONString(resources), e);
        }
        return WebResponseUtils.success(instoreNo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WebResponse editInstore(AppInstoreAddBo resources) {
        try {
            List<AppInstoreAddBo.InstoreDetail> instoreDetailList = resources.getInstoreDetails();

            int totalQty = instoreDetailList.stream().flatMap(instoreDetail -> instoreDetail.getSkuInfoList().stream())
                    .filter(simpleSkuInfo -> Objects.nonNull(simpleSkuInfo.getQty()))
                    .mapToInt(AppInstoreAddBo.SimpleSkuInfo::getQty).sum();
            if (totalQty == 0) {
                return WebResponseUtils.error("入库单数量不能为空");
            }

            StringBuilder things = new StringBuilder();

            //入库单修改
            Instore instore = new Instore();
            instore.setId(resources.getId());
            instore.setRemark(resources.getRemark());
            instore.setQty(totalQty);
            instoreManager.updateById(instore);

            //数据库的入库明细
            List<InstoreDetail> dbInstoreDetailList = instoreDetailManager.getListByInstoreId(resources.getId());

            for (AppInstoreAddBo.InstoreDetail instoreDetail : instoreDetailList) {
                if (Objects.isNull(instoreDetail.getCargoId())) {
                    continue;
                }
                List<AppInstoreAddBo.SimpleSkuInfo> skuInfoList = instoreDetail.getSkuInfoList();
                for (AppInstoreAddBo.SimpleSkuInfo simpleSkuInfo : skuInfoList) {
                    Integer qty = simpleSkuInfo.getQty();
                    Integer skuId = simpleSkuInfo.getSkuId();
                    if (Objects.isNull(qty) || qty == 0) {
                        continue;
                    }
                    AppSku sku = skuManager.getSkuById(skuId);
                    if (Objects.isNull(sku)) {
                        continue;
                    }

                    things.append(instoreDetail.getCargoNo()).append(",");

                    String cargoNo = sku.getCargoNo();
                    Integer cargoId = sku.getCargoId();
                    String skuCode = sku.getSkuCode();
                    String color = sku.getColor();
                    String size = sku.getSize();

                    //根据款号、颜色和尺码与库里数据比较，如果已存在则更新，不存在则新增
                    Optional<InstoreDetail> optional = dbInstoreDetailList.stream().filter(instoreDetail1 -> Objects.equals(skuId, instoreDetail1.getSkuId())).findFirst();

                    //排除参数中明细与库里相同的明细数据，最终得到需要删除的明细数据
                    dbInstoreDetailList = dbInstoreDetailList.stream().filter(instoreDetail1 -> !Objects.equals(skuId, instoreDetail1.getSkuId())).collect(Collectors.toList());

                    int changeQty = 0;
                    if (optional.isPresent()) {
                        InstoreDetail detail = optional.get();

                        InstoreDetail svInstoreDetail = new InstoreDetail();
                        svInstoreDetail.setId(detail.getId());
                        svInstoreDetail.setQty(qty);
                        changeQty = qty - detail.getQty();
                        instoreDetailManager.updateById(svInstoreDetail);
                    } else {
                        InstoreDetail svInstoreDetail = new InstoreDetail();
                        svInstoreDetail.setInstoreId(instore.getId());
                        svInstoreDetail.setCargoNo(cargoNo);
                        svInstoreDetail.setCargoId(cargoId);
                        svInstoreDetail.setSkuId(skuId);
                        svInstoreDetail.setSkuCode(skuCode);
                        svInstoreDetail.setColor(color);
                        svInstoreDetail.setSize(size);
                        svInstoreDetail.setQty(qty);
                        changeQty = qty;
                        instoreDetailManager.save(svInstoreDetail);
                    }
                    //更新库存
                    AppStock appStock = AppStock.builder().skuCode(skuCode).skuId(skuId)
                            .cargoNo(cargoNo).cargoId(cargoId).color(color).size(size).qty(changeQty).totalQty(changeQty)
                            .build();
                    stockManager.saveOrUpdateStock(appStock);
                }
            }
            //已经删除的sku，则扣除对应的库存和删除入库明细数据
            for (InstoreDetail instoreDetail : dbInstoreDetailList) {
                stockManager.plusQtyAndTotalQty(instoreDetail.getSkuId(), 0 - instoreDetail.getQty());
                instoreDetailManager.removeById(instoreDetail.getId());
            }
        } catch (Exception e) {
            log.error("instore edit error，resources:{}", JSON.toJSONString(resources), e);
        }
        return WebResponseUtils.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WebResponse cancelInstore(InstoreCancelBo bo) {
        Instore instore = instoreManager.getById(bo.getInstoreId());
        if (Objects.isNull(instore)) {
            return WebResponseUtils.error("入库查询不存在");
        }
        Instore upInstore = new Instore();
        upInstore.setId(instore.getId());
        upInstore.setStatus(InstoreStatusEnum.CANCEL.getCode());
        instoreManager.updateById(upInstore);

        List<InstoreDetail> instoreDetailList = instoreDetailManager.getListByInstoreId(instore.getId());
        for (InstoreDetail instoreDetail : instoreDetailList) {
            AppStock stock = stockManager.getStockBySkuId(instoreDetail.getSkuId());
            if (Objects.nonNull(stock)) {
                stockManager.subtractQtyAndTotalQty(stock.getId(), instoreDetail.getQty());
            }
        }
        return WebResponseUtils.success();
    }

    @Override
    public WebResponse instoreDetail(String instoreNo) {
        Instore instore = instoreManager.getInstoreByInstoreNo(instoreNo);
        if (Objects.isNull(instore)) {
            return WebResponseUtils.error("入库查询不存在");
        }
        AppInstoreVo appInstoreVo = new AppInstoreVo();
        appInstoreVo.setInstoreNo(instoreNo);
        appInstoreVo.setCreator(instore.getCreator());
        appInstoreVo.setInstoreTime(PloverDateUtil.dateFormatyMdHms(instore.getInstoreTime()));
        appInstoreVo.setTotalQty(instore.getQty());

        List<InstoreDetail> instoreDetailList = instoreDetailManager.getListByInstoreId(instore.getId());
        appInstoreVo.setInstoreDetailList(AppInstoreConvert.convertToDetailVoList(instoreDetailList));
        return WebResponseUtils.success(appInstoreVo);
    }

    @Override
    public WebPageResponse<InstoreStatByInstoreNoVo> statByInstoreNo(InstoreStatPageBo bo) {
        Page page = instoreManager.statByInstoreNo(bo);
        List<InstoreStatByInstoreNoDto> records = page.getRecords();
        if (CollectionUtils.isEmpty(records)) {
            return WebResponseUtils.successPageV2(page);
        }
        List<Integer> instoreIds = records.stream().map(InstoreStatByInstoreNoDto::getId).collect(Collectors.toList());
        List<InstoreDetail> dbInstoreDetailList = instoreDetailManager.getListByInstoreIds(instoreIds);
        List<Integer> cargoIds = dbInstoreDetailList.stream().map(InstoreDetail::getCargoId).collect(Collectors.toList());
        List<AppCargo> cargoList = cargoManager.listByIds(cargoIds);

        List<InstoreStatByInstoreNoVo> result = Lists.newArrayList();
        for (InstoreStatByInstoreNoDto record : records) {
            InstoreStatByInstoreNoVo vo = new InstoreStatByInstoreNoVo();
            String instoreNo = record.getInstoreNo();
            vo.setId(record.getId());
            vo.setInstoreNo(instoreNo);
            vo.setInstoreTime(DateUtil.formatDateTime(record.getInstoreTime()));
            vo.setType(record.getType());
            vo.setTypeText(InstoreTypeEnum.ofCode(record.getType()).getName());
            vo.setCreator(record.getCreator());
            vo.setQty(record.getQty());
            vo.setStatus(record.getStatus());
            InstoreStatusEnum statusEnum = InstoreStatusEnum.ofCode(record.getStatus());
            if (Objects.nonNull(InstoreStatusEnum.ofCode(record.getStatus()))) {
                vo.setStatusText(statusEnum.getName());
            }

            List<InstoreDetail> instoreDetailList = dbInstoreDetailList.stream().filter(instoreDetail -> Objects.equals(instoreDetail.getInstoreId(), record.getId())).collect(Collectors.toList());

            List<InstoreStatByInstoreNoVo.InstoreRecord> instoreRecordList = instoreDetailList.stream().map(instoreDetail -> {
                InstoreStatByInstoreNoVo.InstoreRecord instoreRecord = new InstoreStatByInstoreNoVo.InstoreRecord();
                instoreRecord.setCargoNo(instoreDetail.getCargoNo());
                instoreRecord.setColor(instoreDetail.getColor());
                instoreRecord.setSize(instoreDetail.getSize());
                instoreRecord.setQty(instoreDetail.getQty());

                Optional<AppCargo> cargoOptional = cargoList.stream().filter(cargo -> Objects.equals(cargo.getId(), instoreDetail.getCargoId())).findFirst();
                if (cargoOptional.isPresent()) {
                    AppCargo cargo = cargoOptional.get();
                    instoreRecord.setAmount(AmountUtil.divideHundred(cargo.getPurchasePrice() * instoreDetail.getQty()));
                }
                return instoreRecord;
            }).collect(Collectors.toList());
            vo.setInstoreRecordList(instoreRecordList);
            double totalAmount = instoreRecordList.stream().mapToDouble(value -> new BigDecimal(value.getAmount()).doubleValue()).sum();
            vo.setAmount(String.valueOf(totalAmount));
            result.add(vo);
        }
        page.setRecords(result);
        return WebResponseUtils.successPageV2(page);
    }

    @Override
    public WebResponse statBySku(AppInstoreStatBo instoreStatBo) {
        List<AppInstoreBySkuVo> instoreBySkuVoList = Lists.newArrayList();

        List<InstoreDto> instoreList = instoreManager.getListByDate(instoreStatBo);
        if (CollectionUtils.isEmpty(instoreList)) {
            return WebResponseUtils.successList(instoreBySkuVoList);
        }
        Map<String, List<InstoreDto>> instoreMap = instoreList.stream().collect(Collectors.groupingBy(InstoreDto::getInstoreDate));
        instoreMap.forEach((instoreDate, instoreDtos) -> {
            AppInstoreBySkuVo instoreBySkuVo = new AppInstoreBySkuVo();
            instoreBySkuVo.setInstoreDate(instoreDate);
            int dateTotalQty = instoreDtos.stream().mapToInt(InstoreDto::getQty).sum();
            instoreBySkuVo.setDateTotalQty(dateTotalQty);
            List<AppInstoreBySkuVo.CargoInfo> cargoList = Lists.newArrayList();
            Map<String, List<InstoreDto>> cargoMap = instoreDtos.stream().collect(Collectors.groupingBy(InstoreDto::getCargoNo));
            cargoMap.forEach((cargoNo, skuInfoList) -> {
                AppInstoreBySkuVo.CargoInfo cargoInfo = new AppInstoreBySkuVo.CargoInfo();
                cargoInfo.setCargoNo(cargoNo);
                int cargoTotalQty = skuInfoList.stream().mapToInt(InstoreDto::getQty).sum();
                cargoInfo.setCargoTotalQty(cargoTotalQty);
                List<AppInstoreBySkuVo.SkuInfo> skuInfos = skuInfoList.stream().map(skuInfo -> {
                    AppInstoreBySkuVo.SkuInfo skuInfoVo = new AppInstoreBySkuVo.SkuInfo();
                    skuInfoVo.setColor(skuInfo.getColor());
                    skuInfoVo.setSize(skuInfo.getSize());
                    skuInfoVo.setQty(skuInfo.getQty());
                    return skuInfoVo;
                }).collect(Collectors.toList());
                cargoInfo.setSkuInfoList(skuInfos);
                cargoList.add(cargoInfo);
            });
            instoreBySkuVo.setCargoList(cargoList);
            instoreBySkuVoList.add(instoreBySkuVo);
        });
        return WebResponseUtils.successList(instoreBySkuVoList);
    }

    @Override
    public WebResponse getInstoreDetail(Integer instoreId) {
        Instore instore = instoreManager.getById(instoreId);
        if (Objects.isNull(instore)) {
            return WebResponseUtils.error("入库单不存在");
        }

        InstoreDetailVo vo = new InstoreDetailVo();
        InstoreSimpleVo instoreSimpleVo = instoreMapStruct.entityToSimpleVo(instore);
        vo.setInstore(instoreSimpleVo);

        List<InstoreDetail> instoreDetailList = instoreDetailManager.getListByInstoreId(instoreId);

        List<InstoreDetailVo.Detail> detailList = Lists.newArrayList();
        List<Integer> cargoIds = instoreDetailList.stream().map(InstoreDetail::getCargoId).distinct().collect(Collectors.toList());
        List<AppCargo> cargoList = cargoManager.listByIds(cargoIds);

        if (CollectionUtils.isEmpty(instoreDetailList)) {
            return WebResponseUtils.success(vo);
        }
        Map<Integer, List<InstoreDetail>> cargoMap = instoreDetailList.stream().collect(Collectors.groupingBy(InstoreDetail::getCargoId));
        cargoMap.forEach((cargoId, instoreDetails) -> {
            InstoreDetailVo.Detail detailVo = new InstoreDetailVo.Detail();
            detailVo.setCargoId(cargoId);

            Optional<AppCargo> cargoOptional = cargoList.stream().filter(o -> Objects.equals(o.getId(), cargoId)).findFirst();
            if (cargoOptional.isPresent()) {
                AppCargo appCargo = cargoOptional.get();
                detailVo.setCargoNo(appCargo.getCargoNo());
                detailVo.setCargoName(appCargo.getRemark());
                detailVo.setPicUrl(appCargo.getPicUrl());
            }
            List<InstoreDetailVo.SkuInfo> skuInfoList = instoreDetails.stream().map(detail -> {
                InstoreDetailVo.SkuInfo skuInfo = new InstoreDetailVo.SkuInfo();
                skuInfo.setId(detail.getId());
                skuInfo.setSkuId(detail.getSkuId());
                skuInfo.setQty(detail.getQty());
                skuInfo.setColor(detail.getColor());
                skuInfo.setSize(detail.getSize());
                return skuInfo;
            }).collect(Collectors.toList());
            detailVo.setSkuInfoList(skuInfoList);
            detailList.add(detailVo);
        });
        List<InstoreDetailVo.Detail> details = detailList.stream().sorted(Comparator.comparing(InstoreDetailVo.Detail::getCargoNo)).collect(Collectors.toList());
        vo.setDetailList(details);
        return WebResponseUtils.success(vo);
    }

    private void handlerPostInstore(Instore instore, Integer totalQty, AppInstoreAddBo instoreAddBo, JwtUserDto jwtUser) {
        ThreadUtil.execAsync(() -> {
            String date = PloverDateUtil.dateToDisplayTime(instore.getInstoreTime());
            AppNews appNews = new AppNews();
            appNews.setBossId(jwtUser.getAppBoss().getId());
            appNews.setContent(String.format(Constants.INSTORE_NEWS_TEMPLATE, jwtUser.getAppBoss().getName(), date, totalQty));
            appNewsService.addNews(appNews);
        });
    }


}